#!/usr/bin/env python3
# -*- coding=utf-8 -*-

'''
NOTICE: do not use logging.info/debug...
this would create a default streamhandler
'''

import logging.config
import os
import os.path
import sys
import logging

from .styler import *

_fpath = os.path.dirname(os.path.realpath(__file__))

# logging.basicConfig would create default streamhandler, do not use it
# logging.basicConfig(format=def_fmt, datefmt=def_datefmt)

class ColorStreamHandler(logging.StreamHandler):
    def __init__(self, stream=None, logcolor_config:dict=None):
        super().__init__(stream)
        self._logcolor_config = logcolor_config

    def _decorate(self, level, mesg):
        decorator = None
        if self._logcolor_config and level in self._logcolor_config:
            decorator = self._logcolor_config[level]
        return decorator(mesg) if decorator else mesg

    def emit(self, record) -> None:
        try:
            msg = self.format(record)
            msg = self._decorate(record.levelno, msg)
            stream = self.stream
            stream.write(msg + self.terminator)
            self.flush()
        except RecursionError:  # See issue 36272
            raise
        except Exception:
            self.handleError(record)

def_fmt = "[%(name)s][%(asctime)s] [task: %(process)d:%(thread)d] [%(name)s:%(levelname)8s] %(filename)s:%(lineno)d %(funcName)s: %(message)s"
def_fmt = "[%(name)s][%(asctime)s] [task: %(process)d:%(thread)d] [%(name)s:%(levelname)-8s] %(filename)s:%(lineno)d %(funcName)s: %(message)s"
def_fmt = '[%(name)s][%(asctime)s] [%(levelname)-6s] %(message)s'
def_datefmt = '%Y-%m-%d %H:%M:%S'

predefine_formaters = (
    logging.Formatter(
        '日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】',
        "%Y-%m-%d %H:%M:%S"),
    logging.Formatter(
        '%(asctime)s - %(name)s - %(filename)s - %(funcName)s - %(lineno)d - %(levelname)s - %(message)s',
        "%Y-%m-%d %H:%M:%S"),
    logging.Formatter(
        '%(asctime)s - %(name)s - 【 File "%(pathname)s", line %(lineno)d, in %(funcName)s 】 - %(levelname)s - %(message)s',
        "%Y-%m-%d %H:%M:%S"),  # 一个模仿traceback异常的可跳转到打印日志地方的模板
    logging.Formatter(
        '%(asctime)s - %(name)s - "%(filename)s" - %(funcName)s - %(lineno)d - %(levelname)s - %(message)s - File "%(pathname)s", line %(lineno)d ',
        "%Y-%m-%d %H:%M:%S"),  # 这个也支持日志跳转
    logging.Formatter(
        '%(asctime)s - %(name)s - "%(pathname)s:%(lineno)d" - %(funcName)s - %(levelname)s - %(message)s',
        "%Y-%m-%d %H:%M:%S"),  # 我认为的最好的模板,推荐
    logging.Formatter('%(name)s - %(asctime)-15s - %(filename)s - %(lineno)d - %(levelname)s: %(message)s',
                        "%Y-%m-%d %H:%M:%S"),
    logging.Formatter(
        '%(asctime)s - %(name)s - "%(filename)s:%(lineno)d" - %(levelname)s - %(message)s',
        "%Y-%m-%d %H:%M:%S"),  # 一个只显示简短文件名和所处行数的日志模板

    logging.Formatter(
        '[p%(process)d_t%(thread)d] %(asctime)s - %(name)s - "%(pathname)s:%(lineno)d" - %(funcName)s - %(levelname)s - %(message)s',
        "%Y-%m-%d %H:%M:%S"),  # 对5改进，带进程和线程显示的日志模板。
    logging.Formatter(
        '[p%(process)d_t%(thread)d] %(asctime)s - %(name)s - "%(filename)s:%(lineno)d" - %(levelname)s - %(message)s',
        "%Y-%m-%d %H:%M:%S"),  # 对7改进，带进程和线程显示的日志模板。
    logging.Formatter(
        f'[p%(process)d_t%(thread)d] %(asctime)s - %(name)s - "%(filename)s:%(lineno)d" - %(funcName)s - %(levelname)s - %(message)s',
        "%Y-%m-%d %H:%M:%S"),  # 对7改进，带进程和线程显示的日志模板以及ip和主机名。
)

default_logcolor_config = {
    logging.DEBUG: Styler(F_BLUE),
    logging.INFO: Styler(F_GREEN),
    logging.WARNING: Styler(F_YELLOW),
    logging.ERROR: Styler(F_RED),
    logging.CRITICAL: Styler(F_RED, A_BOLD, A_BLINK),
}

def _path_create(dirpath):
    if not os.path.exists(dirpath):
        os.makedirs(dirpath)
        return True
    return False

def make_stream_handler(loglevel=logging.INFO,
                        formatter=None,
                        stream=sys.stdout):

    hdr = logging.StreamHandler(stream)

    hdr.setLevel(loglevel)
    if formatter:
        hdr.setFormatter(formatter)

    return hdr

def make_color_stream_handler(loglevel=logging.INFO,
                              formatter=None,
                              stream=sys.stdout,
                              logcolor_config:dict=None):
    hdr = ColorStreamHandler(stream, logcolor_config)

    hdr.setLevel(loglevel)
    if formatter:
        hdr.setFormatter(formatter)

    return hdr

def make_file_handler(filepath, loglevel=logging.DEBUG,
                      formatter=None,
                      mode='a', encoding='utf-8',
                      delay=False, errors=None):
    _path_create(os.path.dirname(filepath))

    fh = logging.FileHandler(filepath, mode, encoding,
                            delay=delay, errors=errors)
    if formatter:
        fh.setFormatter(formatter)
    fh.setLevel(loglevel)

    return fh

def make_rotating_file_handler(filepath, loglevel=logging.INFO,
                               formatter=None,
                               maxBytes=0, backupCount=0,
                               mode='a', encoding='utf-8',
                               delay=False, errors=None):
    _path_create(os.path.dirname(filepath))
    rh = logging.handlers.RotatingFileHandler(filepath, mode,
                    maxBytes, backupCount,
                    encoding,
                    delay=delay, errors=errors)
    if formatter:
        rh.setFormatter(formatter)
    rh.setLevel(loglevel)

    return rh

def add_dump_stack(logger):
    def _dump_stack(level=logging.INFO, msg=''):
        logger.log(level, msg, stack_info=True)

    funcname = 'dump_stack'
    if hasattr(logger, funcname):
        return False
    setattr(logger, funcname, _dump_stack)
    return True

def add_dump_exc(logger):
    def _dump_exc(level=logging.ERROR, msg=''):
        ''' use exception '''
        logger.log(level, msg, exc_info=True)

    funcname = 'dump_exc'
    if hasattr(logger, funcname):
        return False
    setattr(logger, funcname, _dump_exc)
    return True

def get_logger(name=None) -> logging.Logger:
    logger = logging.getLogger(name)
    add_dump_exc(logger)
    add_dump_stack(logger)
    return logger

def get_all_logger_names():
    return (k for k in logging.Logger.manager.loggerDict.keys())

def get_loggers(_file, _name, propagate=True) -> tuple[logging.Logger]:
    assert _file
    assert _name

    program = f'Prog/{os.getpid()}'

    bname = os.path.basename(_file)
    pos = bname.rfind('.')
    if pos > 0:
        bname = bname[:pos]

    if _name == '__main__':
        _name = '/'.join([program, 'main'])

    root_logger = get_logger()

    program_logger = get_logger(program)
    program_logger.propagate = propagate

    module_logger = get_logger(_name)
    module_logger.propagate = propagate

    local_logger = get_logger(bname)
    local_logger.propagate = propagate

    return root_logger, program_logger, module_logger, local_logger

_conf_file = os.path.join(_fpath, 'loggers.conf')
if os.path.isfile(_conf_file):
    logging.config.fileConfig(_conf_file, disable_existing_loggers=False)

try:
    from ._setup import *
except ImportError:
    pass
